package com.ruoyi.common.utils.modbus;

import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;

/**
 * modbus4j写入数据
 *
 * @author lsw
 */
public class Modbus4jWriteUtils {

    private static final Logger log = LoggerFactory.getLogger(Modbus4jWriteUtils.class);
    /**
     * 工厂。
     */
    static ModbusFactory modbusFactory;

    static {
        if (modbusFactory == null) {
            modbusFactory = new ModbusFactory();
        }
    }

    static Map<String, ModbusMaster> masterMap = new HashMap<>();

    /**
     * 获取tcpMaster
     *
     * @return ModbusMaster
     * @throws ModbusInitException 异常
     */
    public static ModbusMaster getMaster(String host, int port) throws ModbusInitException {
        ModbusMaster master = masterMap.get(host + port);
        if (master == null) {
            IpParameters params = new IpParameters();
            params.setHost(host);
            params.setPort(port);
            master = modbusFactory.createTcpMaster(params, true);// TCP 协议
            //设置超时时间
            master.setTimeout(100);
            master.init();
            masterMap.put(host + port, master);
        }

        return master;
    }

    /**
     * 写 [01 Coil Status(0x)]写一个 function ID = 5
     *
     * @param slaveId     slave的ID
     * @param writeOffset 位置
     * @param writeValue  值
     * @return 是否写入成功
     * @throws ModbusTransportException 异常
     * @throws ModbusInitException      异常
     */
    public static boolean writeCoil(String host, int port, int slaveId, int writeOffset, boolean writeValue)
            throws ModbusTransportException, ModbusInitException {
        // 获取master
        ModbusMaster tcpMaster = getMaster(host, port);
        // 创建请求
        WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);
        // 发送请求并获取响应对象
        WriteCoilResponse response = (WriteCoilResponse) tcpMaster.send(request);
        return !response.isException();
    }


    /**
     * 写[01 Coil Status(0x)] 写多个 function ID = 15
     *
     * @param slaveId     slaveId
     * @param startOffset 开始位置
     * @param datas       写入的数据
     * @return 是否写入成功
     * @throws ModbusTransportException 异常
     * @throws ModbusInitException      异常
     */
    public static boolean writeCoils(String host, int port, int slaveId, int startOffset, boolean[] datas)
            throws ModbusTransportException, ModbusInitException {
        // 获取master
        ModbusMaster tcpMaster = getMaster(host, port);
        // 创建请求
        WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, datas);
        // 发送请求并获取响应对象
        WriteCoilsResponse response = (WriteCoilsResponse) tcpMaster.send(request);
        return !response.isException();

    }

    /***
     * 写[03 Holding Register(4x)] 写一个 function ID = 6
     *
     * @param slaveId 地址码
     * @param writeOffset 写入寄存器位置
     * @param writeValue 写入的值
     * @return boolean
     * @throws ModbusTransportException 异常
     * @throws ModbusInitException 异常
     */
    public static boolean writeRegister(String host, int port, int slaveId, int writeOffset, short writeValue)
            throws ModbusTransportException, ModbusInitException {
        // 获取master
        ModbusMaster tcpMaster = getMaster(host, port);
        // 创建请求对象
        WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);
        WriteRegisterResponse response = (WriteRegisterResponse) tcpMaster.send(request);
        if (response.isException()) {
            log.error(response.getExceptionMessage());
            return false;
        } else {
            return true;
        }

    }

    /**
     * 写入[03 Holding Register(4x)]写多个 function ID=16
     *
     * @param slaveId     modbus的slaveID
     * @param startOffset 起始位置偏移量值
     * @param datas       写入的数据
     * @return 返回是否写入成功
     * @throws ModbusTransportException 异常
     * @throws ModbusInitException      异常
     */
    public static boolean writeRegisters(String host, int port, int slaveId, int startOffset, short[] datas)
            throws ModbusTransportException, ModbusInitException {
        // 获取master
        ModbusMaster tcpMaster = getMaster(host, port);
        // 创建请求对象
        WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, datas);
        // 发送请求并获取响应对象
        ModbusResponse response = tcpMaster.send(request);
        if (response.isException()) {
            log.error(response.getExceptionMessage());
            return false;
        } else {
            return true;
        }
    }

    /**
     * 写入数字类型的模拟量（如:写入Float类型的模拟量、Double类型模拟量、整数类型Short、Integer、Long）
     *
     * @param slaveId  地址码
     * @param offset   寄存器位置
     * @param value    写入值,Number的子类,例如写入Float浮点类型,Double双精度类型,以及整型short,int,long
     * @param dataType com.serotonin.modbus4j.code.DataType
     * @throws ModbusTransportException 异常
     * @throws ErrorResponseException   异常
     * @throws ModbusInitException      异常
     */
    public static void writeHoldingRegister(String host, int port, int slaveId, int offset, Number value, int dataType)
            throws ModbusTransportException, ErrorResponseException, ModbusInitException {
        // 获取master
        ModbusMaster tcpMaster = getMaster(host, port);
        // 类型
        BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);
        tcpMaster.setValue(locator, value);
    }


    public static void main(String[] args) {

        try {
            String host = "192.168.1.94";
            int port = 502;
            int slaveId = 1;

            // 测试01
            int offset = 0;
            boolean value = true;
            boolean t01 = writeCoil(host, port, slaveId, offset, value);
            System.out.println("T01:" + t01);

            // 测试02
            offset = 1;
            boolean[] values = new boolean[]{true, false, true};
            boolean t02 = writeCoils(host, port, slaveId, offset, values);
            System.out.println("T02:" + t02);

            // 测试03
            offset = 4;
            short v = 600;
            boolean t03 = writeRegister(host, port, slaveId, offset, v);
            System.out.println("T03:" + t03);

            // 测试04
            offset = 5;
            short[] vs = new short[]{-3, 3, 9};
            boolean t04 = writeRegisters(host, port, slaveId, offset, vs);
            System.out.println("t04:" + t04);

            //写模拟量
            offset = 8;
            double v1 = 13.1f;
            writeHoldingRegister(host, port, slaveId, offset, v1, DataType.FOUR_BYTE_FLOAT);

        } catch (Exception e) {
            log.error(String.valueOf(e));
        }

    }

}
